/**
  ******************************************************************************
  * File Name          : ethos.c
  * Description        : 对wizchip驱动做系统级封装
  ******************************************************************************
  * @attention
  * 前缀说明
  * ethos: 系统层接口,仅供ethernet任务使用
  * ethif: 应用层接口,可供其他任务调用
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "bsp.h"
#include "tskcfg.h"
#include "ethernet.h"
#include "gv.h"

const uint8_t socket_bufsize[2][4] = {{2,2,2,2},{2,2,2,2}};
const uint8_t socket_intmask = (Sn_IR_TIMEOUT | Sn_IR_RECV | Sn_IR_DISCON | Sn_IR_CON);

static Ethernet_FeedbackDataStru datafb;

wiz_NetInfo net_info = 
{
  .mac  = {0x00, 0x08, 0xdc, 0x6f, 0x00, 0x8a},
  .ip   = {192, 168, 6,  46},
  .sn   = {255, 255, 255, 0},
  .gw   = {192, 168, 6, 254},
  .dns  = {8, 8, 8, 8},
  .dhcp = NETINFO_STATIC,
};

QueueHandle_t *sn2fbq(uint8_t sn)
{
  switch(sn)
  {
    case 0:
      return ETH_SOCK_0_FBQHD;

    case 1:
      return ETH_SOCK_1_FBQHD;

    case 2:
      return ETH_SOCK_2_FBQHD;

    case 3:
      return ETH_SOCK_3_FBQHD;
  }
  
  return NULL;
}

void print_network_information(void)
{
  wiz_NetInfo netinfo;

	wizchip_getnetinfo(&netinfo);

  EthInfLog("Mac address: %02x:%02x:%02x:%02x:%02x:%02x\r\n",netinfo.mac[0],netinfo.mac[1],netinfo.mac[2],netinfo.mac[3],netinfo.mac[4],netinfo.mac[5]);
  EthInfLog("IP address : %d.%d.%d.%d\r\n",netinfo.ip[0],netinfo.ip[1],netinfo.ip[2],netinfo.ip[3]);
  EthInfLog("Subnet mask: %d.%d.%d.%d\r\n",netinfo.sn[0],netinfo.sn[1],netinfo.sn[2],netinfo.sn[3]);
  EthInfLog("Gate way   : %d.%d.%d.%d\r\n",netinfo.gw[0],netinfo.gw[1],netinfo.gw[2],netinfo.gw[3]);
  EthInfLog("DNS Server : %d.%d.%d.%d\r\n",netinfo.dns[0],netinfo.dns[1],netinfo.dns[2],netinfo.dns[3]);
}

static inline void netinfo_gv_copy(void)
{
  net_info.ip[0] = gv.hr.meas_pa.eth_local_ip[0];
  net_info.ip[1] = gv.hr.meas_pa.eth_local_ip[1];
  net_info.ip[2] = gv.hr.meas_pa.eth_local_ip[2];
  net_info.ip[3] = gv.hr.meas_pa.eth_local_ip[3];
  
  net_info.gw[0] = gv.hr.meas_pa.eth_local_gw[0];
  net_info.gw[1] = gv.hr.meas_pa.eth_local_gw[1];
  net_info.gw[2] = gv.hr.meas_pa.eth_local_gw[2];
  net_info.gw[3] = gv.hr.meas_pa.eth_local_gw[3];
  
  net_info.sn[0] = gv.hr.meas_pa.eth_local_msk[0];
  net_info.sn[1] = gv.hr.meas_pa.eth_local_msk[1];
  net_info.sn[2] = gv.hr.meas_pa.eth_local_msk[2];
  net_info.sn[3] = gv.hr.meas_pa.eth_local_msk[3];
  
//  net_info.mac[0] = gv.hr.meas_pa.eth_local_mac[0];
//  net_info.mac[1] = gv.hr.meas_pa.eth_local_mac[1];
//  net_info.mac[2] = gv.hr.meas_pa.eth_local_mac[2];
//  net_info.mac[3] = gv.hr.meas_pa.eth_local_mac[3];
//  net_info.mac[4] = gv.hr.meas_pa.eth_local_mac[4];
//  net_info.mac[5] = gv.hr.meas_pa.eth_local_mac[5];
}

void ethos_wizchip_initialize(void)
{
  uint8_t tmp1, tmp2;
  intr_kind intmsk = (IK_IP_CONFLICT | IK_DEST_UNREACH | IK_SOCK_0 | IK_SOCK_1 | IK_SOCK_2);
  netmode_type netmod = 0;

  wizchip_spi_init();

  netinfo_gv_copy();

  wizchip_ping_reset();

  tmp1 = getVER();
  if(tmp1 != 0x51){
    EthErrLog("Chip ident:VERSIONR != 0x51, read value = 0x%02x\n", tmp1);
    while(1);
  }

  if(ctlwizchip(CW_INIT_WIZCHIP, (void*)socket_bufsize) == -1){
    EthErrLog(">>>>W5100s memory initialization failed\r\n");
  }

  if(ctlwizchip(CW_SET_INTRMASK, &intmsk) == -1){
    EthErrLog("W5100S interrupt\r\n");
  }

  if(ctlnetwork(CN_SET_NETMODE, &netmod) == -1){
    EthErrLog("netmode set err\r\n");
  }

  /* timeout use chip reset value */
//  if(ctlnetwork(CN_SET_TIMEOUT, &net_tmot) == -1){
//    printf("nettimeout set err\r\n");
//  }

  while(1)
  {
    ctlwizchip(CW_GET_PHYLINK, &tmp1);
    ctlwizchip(CW_GET_PHYLINK, &tmp2);
    if((tmp1==PHY_LINK_ON) && (tmp2==PHY_LINK_ON)){
      break;
    }

    vTaskDelay(500);
  }

  if(net_info.dhcp == NETINFO_DHCP) /* DHCP */
  {
    // wizchip_dhcp_init();
    // while(DHCP_run() != DHCP_IP_LEASED){
    //   vTaskDelay(1000);
    // }
  }
  else /* static */
  {
	  ctlnetwork(CN_SET_NETINFO, &net_info);
    print_network_information();
  }
}

static void socket_isr(uint8_t sn)
{
  uint8_t  sir;
  uint8_t  clsir;
  int32_t  ret;
  QueueHandle_t *fb;
  Ethernet_FeedbackCtrlStru ctrlfb;

  fb = sn2fbq(sn);

  ctlsocket(sn, CS_GET_INTERRUPT, &sir);
  EthDbgLog("socket %u interrupt:0x%04X\r\n", sn, sir);

  if((sir & SIK_CONNECTED) != 0)
  {
    EthDbgLog("socket %u CONNECTED int\r\n", sn);

    clsir = SIK_CONNECTED;
    ctlsocket(sn, CS_CLR_INTERRUPT, &clsir);

    if(fb[CQUE] != NULL){
      ctrlfb.type = ETH_FB_CONNECTED;
      xQueueSend(fb[CQUE], &ctrlfb, 0);
    }
  }

  if((sir & SIK_DISCONNECTED) != 0)
  {
    EthDbgLog("socket %u DISCONNECTED int\r\n", sn);

    clsir = SIK_DISCONNECTED;
    ctlsocket(sn, CS_CLR_INTERRUPT, &clsir);

    if(fb[CQUE] != NULL){
      ctrlfb.type = ETH_FB_DISCONNECTED;
      xQueueSend(fb[CQUE], &ctrlfb, 0);
    }
  }

  if((sir & SIK_RECEIVED) != 0)
  {
    EthDbgLog("socket %u RECEIVED int\r\n", sn);

    if((getSn_MR(sn) & 0x0F) == Sn_MR_TCP){
      ret = recv(sn, datafb.buf, sizeof(datafb.buf));
    }else{
      ret = recvfrom(sn, datafb.buf, sizeof(datafb.buf), datafb.src_ip, &datafb.sec_port);
    }

    if(ret > 0){
      datafb.len = ret;
      if(fb[DQUE] != NULL){
        xQueueSend(fb[DQUE], &datafb, 0);
      }
    }else{
      /* err deal */
    }

    clsir = SIK_RECEIVED;
    ctlsocket(sn, CS_CLR_INTERRUPT, &clsir);
  }

  if((sir & SIK_TIMEOUT) != 0)
  {
    EthDbgLog("socket %u TIMEOUT int\r\n", sn);

    clsir = SIK_TIMEOUT;
    ctlsocket(sn, CS_CLR_INTERRUPT, &clsir);

    if(fb[CQUE] != NULL){
      ctrlfb.type = ETH_FB_TIMEOUT;
      xQueueSend(fb[CQUE], &ctrlfb, 0);
    }
  }

  // if((sir & SIK_SENT) != 0){}
}

void ethos_wizchip_getinterrupt(void)
{
  uint16_t sirir;
  uint8_t  clsir;

  sirir = wizchip_getinterrupt();
  EthDbgLog("interrupt:0x%04X\r\n", sirir);
  
  if((sirir & IK_SOCK_0) != 0)
  {
    socket_isr(0);
  }

  if((sirir & IK_SOCK_1) != 0)
  {
    socket_isr(1);
  }

  if((sirir & IK_SOCK_2) != 0)
  {
    socket_isr(2);
  }

  if((sirir & IK_SOCK_3) != 0)
  {
    EthDbgLog("socket 3 int\r\n");
    
    clsir = SIK_ALL;
    ctlsocket(3, CS_CLR_INTERRUPT, &clsir);
  }

  /* IP 冲突 */
  if((sirir & IK_IP_CONFLICT) != 0)
  {
    EthDbgLog("IP_CONFLICT int\r\n");
  }

  if((sirir & IK_DEST_UNREACH) != 0)
  {
    EthDbgLog("DEST_UNREACH int\r\n");
  }

  wizchip_clrinterrupt((intr_kind)(sirir));
}

void ethos_ctrl_socket(const Ethernet_CtrlQueStru *ctrl)
{
  int8_t err;

  err = socket(ctrl->sn, ctrl->protocol, ctrl->local_port, ctrl->flag);

  if(err < 0){
    EthErrLog("socket %d init error:%d\r\n", ctrl->sn, err);
    return;
  }

  err = ctlsocket(ctrl->sn, CS_SET_INTMASK, (void *)&socket_intmask);
  if(err < 0){
    EthErrLog("socket %d ctrl error:%d\r\n", ctrl->sn, err);
  }
}

void ethos_ctrl_close(const Ethernet_CtrlQueStru *ctrl)
{
  int8_t err;
  
  err = close(ctrl->sn);
  if(err < 0){
    EthErrLog("close socket %d ctrl error:%d\r\n", ctrl->sn, err);
  }
}

void ethos_ctrl_listen(const Ethernet_CtrlQueStru *ctrl)
{
  int8_t err;
  
  err = listen(ctrl->sn);
  if(err < 0){
    EthErrLog("listen socket %d ctrl error:%d\r\n", ctrl->sn, err);
  }
}

void ethos_ctrl_connect(const Ethernet_CtrlQueStru *ctrl)
{
  int8_t err;
  err = connect(ctrl->sn, ctrl->dest_ip, ctrl->dest_port);
  if(err < 0){
    EthErrLog("connect %d ctrl error:%d\r\n", ctrl->sn, err);
  }
}

void ethos_ctrl_disconnect(const Ethernet_CtrlQueStru *ctrl)
{
  int8_t err;

  err = listen(ctrl->sn);
  if(err < 0){
    EthErrLog("disconnect %d ctrl error:%d\r\n", ctrl->sn, err);
  }
}

void ethos_ctrl_getsocketsr(const Ethernet_CtrlQueStru *ctrl)
{
  uint8_t sr = 0;
  
  getsockopt(ctrl->sn, SO_STATUS, &sr);
  ethif_get_sr[ctrl->sn] = sr;
}

void ethos_send(const Ethernet_SendQueStru *tosend)
{
  int32_t retlen;
  uint16_t remlen;
  const uint8_t *pos;

  pos = tosend->buf;
  remlen = tosend->len;

  do
  {
    retlen = send(tosend->sn, pos, remlen);

    if(retlen > 0)
    {
      remlen -= retlen;
      pos += retlen;
    }
    else /* (retlen < 0) err */
    {
      /* err deal */
      return;
    }
  }while(remlen != 0);
}

void ethos_sendto(const Ethernet_SendQueStru *tosend)
{
  int32_t retlen;
  uint16_t remlen;
  const uint8_t *pos;

  pos = tosend->buf;
  remlen = tosend->len;

  do
  {
    retlen = sendto(tosend->sn, pos, remlen, tosend->dest_ip, tosend->dest_port);

    if(retlen > 0)
    {
      remlen -= retlen;
      pos += retlen;
    }
    else /* (retlen < 0) err */
    {
      /* err deal */
      return;
    }
  }while(remlen != 0);
}
